/*
 * Copyright 2012 Claude Houle claude.houle@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.mochaccino.framework.scheduler;

import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.inject.Provider;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.DefaultThreadExecutor;
import org.quartz.impl.DirectSchedulerFactory;
import org.quartz.simpl.RAMJobStore;
import org.quartz.simpl.SimpleThreadPool;
import org.quartz.spi.JobStore;
import org.quartz.spi.SchedulerPlugin;
import org.quartz.spi.ThreadExecutor;
import org.quartz.spi.ThreadPool;

/** Scheduler Provider */
public final class QuartzSchedulerProvider implements Provider<Scheduler> {

	private JobStore                    jobStore                        = new RAMJobStore();
	private Map<String,SchedulerPlugin> plugins                         = new HashMap<String,SchedulerPlugin>();
	private String                      instanceId                      = UUID.randomUUID().toString();
	private String                      jmxObjectName                   = null;
	private String                      name                            = "scheduler";
	private String                      rmiRegistryHost                 = null;
	private ThreadExecutor              threadExecutor                  = new DefaultThreadExecutor();
	private ThreadPool                  threadPool                      = new SimpleThreadPool( 10, Thread.NORM_PRIORITY );
	private boolean                     jmx                             = false;
	private int                         batchSize                       = 1;
	private int                         rmiRegistryPort                 = 0;
	private long                        batchTimeWindow                 = 0L;
	private long                        idleWaitingTime                 = TimeUnit.SECONDS.toMillis( 30 );

	/** Constructor */
	public QuartzSchedulerProvider( String name, String instanceId, JobStore jobStore ) {
		this.jobStore = Preconditions.checkNotNull( jobStore );
		this.name = Preconditions.checkNotNull( name );
		this.instanceId = Preconditions.checkNotNull( instanceId );
	}

	/** Constructor */
	public QuartzSchedulerProvider( String name, String instanceId ) {
		this( name, instanceId, new RAMJobStore() );
	}

	/**
	 * Add Plugin
	 *
	 * @return Previous plugin with specified id. May be null
	 */
	public SchedulerPlugin addPlugin( String id, SchedulerPlugin plugin ) {
		return this.plugins.put( Preconditions.checkNotNull( id ), Preconditions.checkNotNull( plugin ) );
	}

	@Override
	public Scheduler get() {
		try {
			DirectSchedulerFactory factory = DirectSchedulerFactory.getInstance();
			factory.createScheduler( name, instanceId, threadPool, threadExecutor, jobStore, plugins, rmiRegistryHost, rmiRegistryPort, idleWaitingTime, -1L, jmx, jmxObjectName, batchSize, batchTimeWindow );
			return factory.getScheduler( name );
		} catch ( SchedulerException e ) {
			throw new RuntimeException( e.getMessage(), e );
		}
	}

	public void setBatchSize( int batchSize ) {
		this.batchSize = batchSize;
	}

	public void setBatchTimeWindow( long batchTimeWindow, TimeUnit unit ) {
		this.batchTimeWindow = Preconditions.checkNotNull( unit ).toMillis( batchTimeWindow );
	}

	public void setIdleWaitingTime( long idleWaitingTime, TimeUnit unit ) {
		this.idleWaitingTime = Preconditions.checkNotNull( unit ).toMillis( idleWaitingTime );
	}

	public void setJmx( boolean jmx, String jmxObjectName ) {
		this.jmx = jmx;
		this.jmxObjectName = Preconditions.checkNotNull( jmxObjectName );
	}

	public void setRmiRegistryHost( String rmiRegistryHost, int port ) {
		Preconditions.checkState( port > 0 );
		this.rmiRegistryHost = Preconditions.checkNotNull( rmiRegistryHost );
		this.rmiRegistryPort = port;
	}

	public void setThreadExecutor( ThreadExecutor threadExecutor ) {
		this.threadExecutor = Preconditions.checkNotNull( threadExecutor );
	}

	public void setThreadPool( ThreadPool threadPool ) {
		this.threadPool = Preconditions.checkNotNull( threadPool );
	}
}

